package com.dj.product.k2faas.common.utils;

import com.dj.product.k2faas.common.constant.ReflectionConstant;
import com.dj.product.k2faas.common.reflection.SerializableFunction;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by 丁建 on 2025/9/23 08:42
 *
 * @author 丁建
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ReflectionUtil {
    private static final Map<SerializableFunction<?, ?>, Field> CACHE = new ConcurrentHashMap<>();

    public static <T, R> String getFieldName(SerializableFunction<T, R> function) {
        Field field = getField(function);
        return field.getName();
    }

    public static <T, R> Field getField(SerializableFunction<T, R> function) {
        return CACHE.computeIfAbsent(function, ReflectionUtil::findField);
    }

    public static <T, R> Field findField(SerializableFunction<T, R> function) {
        Field field = null;
        String fieldName = null;
        try {
            // 第1步 获取SerializedLambda
            Method method = function.getClass().getDeclaredMethod(ReflectionConstant.WRITE_REPLACE);

            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
            // 第2步 implMethodName 即为Field对应的Getter方法名
            String implMethodName = serializedLambda.getImplMethodName();
            if (implMethodName.startsWith(ReflectionConstant.GET) && implMethodName.length() > ReflectionConstant.GET_LENGTH) {
                fieldName = Introspector.decapitalize(implMethodName.substring(ReflectionConstant.GET_LENGTH));
            } else if (implMethodName.startsWith(ReflectionConstant.IS) && implMethodName.length() > ReflectionConstant.IS_LENGTH) {
                fieldName = Introspector.decapitalize(implMethodName.substring(ReflectionConstant.IS_LENGTH));
            } else if (implMethodName.startsWith(ReflectionConstant.LAMBDA$)) {
                throw new IllegalArgumentException(ReflectionConstant.LAMBDA_ILLEGAL_ARGUMENT_EXCEPTION_MESSAGE);
            } else {
                throw new IllegalArgumentException(implMethodName + ReflectionConstant.GETTER_ILLEGAL_ARGUMENT_EXCEPTION_MESSAGE);
            }
            // 第3步 获取的Class是字符串，并且包名是“/”分割，需要替换成“.”，才能获取到对应的Class对象
            String declaredClass = serializedLambda.getImplClass().replace("/", ".");

            Class<?> aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());
            // 第4步 Spring 中的反射工具类获取Class中定义的Field
            field = ReflectionUtils.findField(aClass, fieldName);

        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        // 第5步 如果没有找到对应的字段应该抛出异常
        if (field != null) {
            return field;
        }

        throw new NoSuchFieldError(fieldName);
    }
}
